home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / lib / python2.6 / zipfile.py < prev    next >
Text File  |  2009-11-02  |  53KB  |  1,407 lines

  1. """
  2. Read and write ZIP files.
  3. """
  4. import struct, os, time, sys, shutil
  5. import binascii, cStringIO, stat
  6.  
  7. try:
  8.     import zlib # We may need its compression method
  9.     crc32 = zlib.crc32
  10. except ImportError:
  11.     zlib = None
  12.     crc32 = binascii.crc32
  13.  
  14. __all__ = ["BadZipfile", "error", "ZIP_STORED", "ZIP_DEFLATED", "is_zipfile",
  15.            "ZipInfo", "ZipFile", "PyZipFile", "LargeZipFile" ]
  16.  
  17. class BadZipfile(Exception):
  18.     pass
  19.  
  20.  
  21. class LargeZipFile(Exception):
  22.     """
  23.     Raised when writing a zipfile, the zipfile requires ZIP64 extensions
  24.     and those extensions are disabled.
  25.     """
  26.  
  27. error = BadZipfile      # The exception raised by this module
  28.  
  29. ZIP64_LIMIT = (1 << 31) - 1
  30. ZIP_FILECOUNT_LIMIT = 1 << 16
  31. ZIP_MAX_COMMENT = (1 << 16) - 1
  32.  
  33. # constants for Zip file compression methods
  34. ZIP_STORED = 0
  35. ZIP_DEFLATED = 8
  36. # Other ZIP compression methods not supported
  37.  
  38. # Below are some formats and associated data for reading/writing headers using
  39. # the struct module.  The names and structures of headers/records are those used
  40. # in the PKWARE description of the ZIP file format:
  41. #     http://www.pkware.com/documents/casestudies/APPNOTE.TXT
  42. # (URL valid as of January 2008)
  43.  
  44. # The "end of central directory" structure, magic number, size, and indices
  45. # (section V.I in the format document)
  46. structEndArchive = "<4s4H2LH"
  47. stringEndArchive = "PK\005\006"
  48. sizeEndCentDir = struct.calcsize(structEndArchive)
  49.  
  50. _ECD_SIGNATURE = 0
  51. _ECD_DISK_NUMBER = 1
  52. _ECD_DISK_START = 2
  53. _ECD_ENTRIES_THIS_DISK = 3
  54. _ECD_ENTRIES_TOTAL = 4
  55. _ECD_SIZE = 5
  56. _ECD_OFFSET = 6
  57. _ECD_COMMENT_SIZE = 7
  58. # These last two indices are not part of the structure as defined in the
  59. # spec, but they are used internally by this module as a convenience
  60. _ECD_COMMENT = 8
  61. _ECD_LOCATION = 9
  62.  
  63. # The "central directory" structure, magic number, size, and indices
  64. # of entries in the structure (section V.F in the format document)
  65. structCentralDir = "<4s4B4HL2L5H2L"
  66. stringCentralDir = "PK\001\002"
  67. sizeCentralDir = struct.calcsize(structCentralDir)
  68.  
  69. # indexes of entries in the central directory structure
  70. _CD_SIGNATURE = 0
  71. _CD_CREATE_VERSION = 1
  72. _CD_CREATE_SYSTEM = 2
  73. _CD_EXTRACT_VERSION = 3
  74. _CD_EXTRACT_SYSTEM = 4
  75. _CD_FLAG_BITS = 5
  76. _CD_COMPRESS_TYPE = 6
  77. _CD_TIME = 7
  78. _CD_DATE = 8
  79. _CD_CRC = 9
  80. _CD_COMPRESSED_SIZE = 10
  81. _CD_UNCOMPRESSED_SIZE = 11
  82. _CD_FILENAME_LENGTH = 12
  83. _CD_EXTRA_FIELD_LENGTH = 13
  84. _CD_COMMENT_LENGTH = 14
  85. _CD_DISK_NUMBER_START = 15
  86. _CD_INTERNAL_FILE_ATTRIBUTES = 16
  87. _CD_EXTERNAL_FILE_ATTRIBUTES = 17
  88. _CD_LOCAL_HEADER_OFFSET = 18
  89.  
  90. # The "local file header" structure, magic number, size, and indices
  91. # (section V.A in the format document)
  92. structFileHeader = "<4s2B4HL2L2H"
  93. stringFileHeader = "PK\003\004"
  94. sizeFileHeader = struct.calcsize(structFileHeader)
  95.  
  96. _FH_SIGNATURE = 0
  97. _FH_EXTRACT_VERSION = 1
  98. _FH_EXTRACT_SYSTEM = 2
  99. _FH_GENERAL_PURPOSE_FLAG_BITS = 3
  100. _FH_COMPRESSION_METHOD = 4
  101. _FH_LAST_MOD_TIME = 5
  102. _FH_LAST_MOD_DATE = 6
  103. _FH_CRC = 7
  104. _FH_COMPRESSED_SIZE = 8
  105. _FH_UNCOMPRESSED_SIZE = 9
  106. _FH_FILENAME_LENGTH = 10
  107. _FH_EXTRA_FIELD_LENGTH = 11
  108.  
  109. # The "Zip64 end of central directory locator" structure, magic number, and size
  110. structEndArchive64Locator = "<4sLQL"
  111. stringEndArchive64Locator = "PK\x06\x07"
  112. sizeEndCentDir64Locator = struct.calcsize(structEndArchive64Locator)
  113.  
  114. # The "Zip64 end of central directory" record, magic number, size, and indices
  115. # (section V.G in the format document)
  116. structEndArchive64 = "<4sQ2H2L4Q"
  117. stringEndArchive64 = "PK\x06\x06"
  118. sizeEndCentDir64 = struct.calcsize(structEndArchive64)
  119.  
  120. _CD64_SIGNATURE = 0
  121. _CD64_DIRECTORY_RECSIZE = 1
  122. _CD64_CREATE_VERSION = 2
  123. _CD64_EXTRACT_VERSION = 3
  124. _CD64_DISK_NUMBER = 4
  125. _CD64_DISK_NUMBER_START = 5
  126. _CD64_NUMBER_ENTRIES_THIS_DISK = 6
  127. _CD64_NUMBER_ENTRIES_TOTAL = 7
  128. _CD64_DIRECTORY_SIZE = 8
  129. _CD64_OFFSET_START_CENTDIR = 9
  130.  
  131. def is_zipfile(filename):
  132.     """Quickly see if file is a ZIP file by checking the magic number."""
  133.     try:
  134.         fpin = open(filename, "rb")
  135.         endrec = _EndRecData(fpin)
  136.         fpin.close()
  137.         if endrec:
  138.             return True                 # file has correct magic number
  139.     except IOError:
  140.         pass
  141.     return False
  142.  
  143. def _EndRecData64(fpin, offset, endrec):
  144.     """
  145.     Read the ZIP64 end-of-archive records and use that to update endrec
  146.     """
  147.     fpin.seek(offset - sizeEndCentDir64Locator, 2)
  148.     data = fpin.read(sizeEndCentDir64Locator)
  149.     sig, diskno, reloff, disks = struct.unpack(structEndArchive64Locator, data)
  150.     if sig != stringEndArchive64Locator:
  151.         return endrec
  152.  
  153.     if diskno != 0 or disks != 1:
  154.         raise BadZipfile("zipfiles that span multiple disks are not supported")
  155.  
  156.     # Assume no 'zip64 extensible data'
  157.     fpin.seek(offset - sizeEndCentDir64Locator - sizeEndCentDir64, 2)
  158.     data = fpin.read(sizeEndCentDir64)
  159.     sig, sz, create_version, read_version, disk_num, disk_dir, \
  160.             dircount, dircount2, dirsize, diroffset = \
  161.             struct.unpack(structEndArchive64, data)
  162.     if sig != stringEndArchive64:
  163.         return endrec
  164.  
  165.     # Update the original endrec using data from the ZIP64 record
  166.     endrec[_ECD_SIGNATURE] = sig
  167.     endrec[_ECD_DISK_NUMBER] = disk_num
  168.     endrec[_ECD_DISK_START] = disk_dir
  169.     endrec[_ECD_ENTRIES_THIS_DISK] = dircount
  170.     endrec[_ECD_ENTRIES_TOTAL] = dircount2
  171.     endrec[_ECD_SIZE] = dirsize
  172.     endrec[_ECD_OFFSET] = diroffset
  173.     return endrec
  174.  
  175.  
  176. def _EndRecData(fpin):
  177.     """Return data from the "End of Central Directory" record, or None.
  178.  
  179.     The data is a list of the nine items in the ZIP "End of central dir"
  180.     record followed by a tenth item, the file seek offset of this record."""
  181.  
  182.     # Determine file size
  183.     fpin.seek(0, 2)
  184.     filesize = fpin.tell()
  185.  
  186.     # Check to see if this is ZIP file with no archive comment (the
  187.     # "end of central directory" structure should be the last item in the
  188.     # file if this is the case).
  189.     fpin.seek(-sizeEndCentDir, 2)
  190.     data = fpin.read()
  191.     if data[0:4] == stringEndArchive and data[-2:] == "\000\000":
  192.         # the signature is correct and there's no comment, unpack structure
  193.         endrec = struct.unpack(structEndArchive, data)
  194.         endrec=list(endrec)
  195.  
  196.         # Append a blank comment and record start offset
  197.         endrec.append("")
  198.         endrec.append(filesize - sizeEndCentDir)
  199.  
  200.         # Try to read the "Zip64 end of central directory" structure
  201.         return _EndRecData64(fpin, -sizeEndCentDir, endrec)
  202.  
  203.     # Either this is not a ZIP file, or it is a ZIP file with an archive
  204.     # comment.  Search the end of the file for the "end of central directory"
  205.     # record signature. The comment is the last item in the ZIP file and may be
  206.     # up to 64K long.  It is assumed that the "end of central directory" magic
  207.     # number does not appear in the comment.
  208.     maxCommentStart = max(filesize - (1 << 16) - sizeEndCentDir, 0)
  209.     fpin.seek(maxCommentStart, 0)
  210.     data = fpin.read()
  211.     start = data.rfind(stringEndArchive)
  212.     if start >= 0:
  213.         # found the magic number; attempt to unpack and interpret
  214.         recData = data[start:start+sizeEndCentDir]
  215.         endrec = list(struct.unpack(structEndArchive, recData))
  216.         comment = data[start+sizeEndCentDir:]
  217.         # check that comment length is correct
  218.         if endrec[_ECD_COMMENT_SIZE] == len(comment):
  219.             # Append the archive comment and start offset
  220.             endrec.append(comment)
  221.             endrec.append(maxCommentStart + start)
  222.  
  223.             # Try to read the "Zip64 end of central directory" structure
  224.             return _EndRecData64(fpin, maxCommentStart + start - filesize,
  225.                                  endrec)
  226.  
  227.     # Unable to find a valid end of central directory structure
  228.     return
  229.  
  230.  
  231. class ZipInfo (object):
  232.     """Class with attributes describing each file in the ZIP archive."""
  233.  
  234.     __slots__ = (
  235.             'orig_filename',
  236.             'filename',
  237.             'date_time',
  238.             'compress_type',
  239.             'comment',
  240.             'extra',
  241.             'create_system',
  242.             'create_version',
  243.             'extract_version',
  244.             'reserved',
  245.             'flag_bits',
  246.             'volume',
  247.             'internal_attr',
  248.             'external_attr',
  249.             'header_offset',
  250.             'CRC',
  251.             'compress_size',
  252.             'file_size',
  253.             '_raw_time',
  254.         )
  255.  
  256.     def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
  257.         self.orig_filename = filename   # Original file name in archive
  258.  
  259.         # Terminate the file name at the first null byte.  Null bytes in file
  260.         # names are used as tricks by viruses in archives.
  261.         null_byte = filename.find(chr(0))
  262.         if null_byte >= 0:
  263.             filename = filename[0:null_byte]
  264.         # This is used to ensure paths in generated ZIP files always use
  265.         # forward slashes as the directory separator, as required by the
  266.         # ZIP format specification.
  267.         if os.sep != "/" and os.sep in filename:
  268.             filename = filename.replace(os.sep, "/")
  269.  
  270.         self.filename = filename        # Normalized file name
  271.         self.date_time = date_time      # year, month, day, hour, min, sec
  272.         # Standard values:
  273.         self.compress_type = ZIP_STORED # Type of compression for the file
  274.         self.comment = ""               # Comment for each file
  275.         self.extra = ""                 # ZIP extra data
  276.         if sys.platform == 'win32':
  277.             self.create_system = 0          # System which created ZIP archive
  278.         else:
  279.             # Assume everything else is unix-y
  280.             self.create_system = 3          # System which created ZIP archive
  281.         self.create_version = 20        # Version which created ZIP archive
  282.         self.extract_version = 20       # Version needed to extract archive
  283.         self.reserved = 0               # Must be zero
  284.         self.flag_bits = 0              # ZIP flag bits
  285.         self.volume = 0                 # Volume number of file header
  286.         self.internal_attr = 0          # Internal attributes
  287.         self.external_attr = 0          # External file attributes
  288.         # Other attributes are set by class ZipFile:
  289.         # header_offset         Byte offset to the file header
  290.         # CRC                   CRC-32 of the uncompressed file
  291.         # compress_size         Size of the compressed file
  292.         # file_size             Size of the uncompressed file
  293.  
  294.     def FileHeader(self):
  295.         """Return the per-file header as a string."""
  296.         dt = self.date_time
  297.         dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
  298.         dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
  299.         if self.flag_bits & 0x08:
  300.             # Set these to zero because we write them after the file data
  301.             CRC = compress_size = file_size = 0
  302.         else:
  303.             CRC = self.CRC
  304.             compress_size = self.compress_size
  305.             file_size = self.file_size
  306.  
  307.         extra = self.extra
  308.  
  309.         if file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT:
  310.             # File is larger than what fits into a 4 byte integer,
  311.             # fall back to the ZIP64 extension
  312.             fmt = '<HHQQ'
  313.             extra = extra + struct.pack(fmt,
  314.                     1, struct.calcsize(fmt)-4, file_size, compress_size)
  315.             file_size = 0xffffffff
  316.             compress_size = 0xffffffff
  317.             self.extract_version = max(45, self.extract_version)
  318.             self.create_version = max(45, self.extract_version)
  319.  
  320.         filename, flag_bits = self._encodeFilenameFlags()
  321.         header = struct.pack(structFileHeader, stringFileHeader,
  322.                  self.extract_version, self.reserved, flag_bits,
  323.                  self.compress_type, dostime, dosdate, CRC,
  324.                  compress_size, file_size,
  325.                  len(filename), len(extra))
  326.         return header + filename + extra
  327.  
  328.     def _encodeFilenameFlags(self):
  329.         if isinstance(self.filename, unicode):
  330.             try:
  331.                 return self.filename.encode('ascii'), self.flag_bits
  332.             except UnicodeEncodeError:
  333.                 return self.filename.encode('utf-8'), self.flag_bits | 0x800
  334.         else:
  335.             return self.filename, self.flag_bits
  336.  
  337.     def _decodeFilename(self):
  338.         if self.flag_bits & 0x800:
  339.             return self.filename.decode('utf-8')
  340.         else:
  341.             return self.filename
  342.  
  343.     def _decodeExtra(self):
  344.         # Try to decode the extra field.
  345.         extra = self.extra
  346.         unpack = struct.unpack
  347.         while extra:
  348.             tp, ln = unpack('<HH', extra[:4])
  349.             if tp == 1:
  350.                 if ln >= 24:
  351.                     counts = unpack('<QQQ', extra[4:28])
  352.                 elif ln == 16:
  353.                     counts = unpack('<QQ', extra[4:20])
  354.                 elif ln == 8:
  355.                     counts = unpack('<Q', extra[4:12])
  356.                 elif ln == 0:
  357.                     counts = ()
  358.                 else:
  359.                     raise RuntimeError, "Corrupt extra field %s"%(ln,)
  360.  
  361.                 idx = 0
  362.  
  363.                 # ZIP64 extension (large files and/or large archives)
  364.                 if self.file_size in (0xffffffffffffffffL, 0xffffffffL):
  365.                     self.file_size = counts[idx]
  366.                     idx += 1
  367.  
  368.                 if self.compress_size == 0xFFFFFFFFL:
  369.                     self.compress_size = counts[idx]
  370.                     idx += 1
  371.  
  372.                 if self.header_offset == 0xffffffffL:
  373.                     old = self.header_offset
  374.                     self.header_offset = counts[idx]
  375.                     idx+=1
  376.  
  377.             extra = extra[ln+4:]
  378.  
  379.  
  380. class _ZipDecrypter:
  381.     """Class to handle decryption of files stored within a ZIP archive.
  382.  
  383.     ZIP supports a password-based form of encryption. Even though known
  384.     plaintext attacks have been found against it, it is still useful
  385.     to be able to get data out of such a file.
  386.  
  387.     Usage:
  388.         zd = _ZipDecrypter(mypwd)
  389.         plain_char = zd(cypher_char)
  390.         plain_text = map(zd, cypher_text)
  391.     """
  392.  
  393.     def _GenerateCRCTable():
  394.         """Generate a CRC-32 table.
  395.  
  396.         ZIP encryption uses the CRC32 one-byte primitive for scrambling some
  397.         internal keys. We noticed that a direct implementation is faster than
  398.         relying on binascii.crc32().
  399.         """
  400.         poly = 0xedb88320
  401.         table = [0] * 256
  402.         for i in range(256):
  403.             crc = i
  404.             for j in range(8):
  405.                 if crc & 1:
  406.                     crc = ((crc >> 1) & 0x7FFFFFFF) ^ poly
  407.                 else:
  408.                     crc = ((crc >> 1) & 0x7FFFFFFF)
  409.             table[i] = crc
  410.         return table
  411.     crctable = _GenerateCRCTable()
  412.  
  413.     def _crc32(self, ch, crc):
  414.         """Compute the CRC32 primitive on one byte."""
  415.         return ((crc >> 8) & 0xffffff) ^ self.crctable[(crc ^ ord(ch)) & 0xff]
  416.  
  417.     def __init__(self, pwd):
  418.         self.key0 = 305419896
  419.         self.key1 = 591751049
  420.         self.key2 = 878082192
  421.         for p in pwd:
  422.             self._UpdateKeys(p)
  423.  
  424.     def _UpdateKeys(self, c):
  425.         self.key0 = self._crc32(c, self.key0)
  426.         self.key1 = (self.key1 + (self.key0 & 255)) & 4294967295
  427.         self.key1 = (self.key1 * 134775813 + 1) & 4294967295
  428.         self.key2 = self._crc32(chr((self.key1 >> 24) & 255), self.key2)
  429.  
  430.     def __call__(self, c):
  431.         """Decrypt a single character."""
  432.         c = ord(c)
  433.         k = self.key2 | 2
  434.         c = c ^ (((k * (k^1)) >> 8) & 255)
  435.         c = chr(c)
  436.         self._UpdateKeys(c)
  437.         return c
  438.  
  439. class ZipExtFile:
  440.     """File-like object for reading an archive member.
  441.        Is returned by ZipFile.open().
  442.     """
  443.  
  444.     def __init__(self, fileobj, zipinfo, decrypt=None):
  445.         self.fileobj = fileobj
  446.         self.decrypter = decrypt
  447.         self.bytes_read = 0L
  448.         self.rawbuffer = ''
  449.         self.readbuffer = ''
  450.         self.linebuffer = ''
  451.         self.eof = False
  452.         self.univ_newlines = False
  453.         self.nlSeps = ("\n", )
  454.         self.lastdiscard = ''
  455.  
  456.         self.compress_type = zipinfo.compress_type
  457.         self.compress_size = zipinfo.compress_size
  458.  
  459.         self.closed  = False
  460.         self.mode    = "r"
  461.         self.name = zipinfo.filename
  462.  
  463.         # read from compressed files in 64k blocks
  464.         self.compreadsize = 64*1024
  465.         if self.compress_type == ZIP_DEFLATED:
  466.             self.dc = zlib.decompressobj(-15)
  467.  
  468.     def set_univ_newlines(self, univ_newlines):
  469.         self.univ_newlines = univ_newlines
  470.  
  471.         # pick line separator char(s) based on universal newlines flag
  472.         self.nlSeps = ("\n", )
  473.         if self.univ_newlines:
  474.             self.nlSeps = ("\r\n", "\r", "\n")
  475.  
  476.     def __iter__(self):
  477.         return self
  478.  
  479.     def next(self):
  480.         nextline = self.readline()
  481.         if not nextline:
  482.             raise StopIteration()
  483.  
  484.         return nextline
  485.  
  486.     def close(self):
  487.         self.closed = True
  488.  
  489.     def _checkfornewline(self):
  490.         nl, nllen = -1, -1
  491.         if self.linebuffer:
  492.             # ugly check for cases where half of an \r\n pair was
  493.             # read on the last pass, and the \r was discarded.  In this
  494.             # case we just throw away the \n at the start of the buffer.
  495.             if (self.lastdiscard, self.linebuffer[0]) == ('\r','\n'):
  496.                 self.linebuffer = self.linebuffer[1:]
  497.  
  498.             for sep in self.nlSeps:
  499.                 nl = self.linebuffer.find(sep)
  500.                 if nl >= 0:
  501.                     nllen = len(sep)
  502.                     return nl, nllen
  503.  
  504.         return nl, nllen
  505.  
  506.     def readline(self, size = -1):
  507.         """Read a line with approx. size. If size is negative,
  508.            read a whole line.
  509.         """
  510.         if size < 0:
  511.             size = sys.maxint
  512.         elif size == 0:
  513.             return ''
  514.  
  515.         # check for a newline already in buffer
  516.         nl, nllen = self._checkfornewline()
  517.  
  518.         if nl >= 0:
  519.             # the next line was already in the buffer
  520.             nl = min(nl, size)
  521.         else:
  522.             # no line break in buffer - try to read more
  523.             size -= len(self.linebuffer)
  524.             while nl < 0 and size > 0:
  525.                 buf = self.read(min(size, 100))
  526.                 if not buf:
  527.                     break
  528.                 self.linebuffer += buf
  529.                 size -= len(buf)
  530.  
  531.                 # check for a newline in buffer
  532.                 nl, nllen = self._checkfornewline()
  533.  
  534.             # we either ran out of bytes in the file, or
  535.             # met the specified size limit without finding a newline,
  536.             # so return current buffer
  537.             if nl < 0:
  538.                 s = self.linebuffer
  539.                 self.linebuffer = ''
  540.                 return s
  541.  
  542.         buf = self.linebuffer[:nl]
  543.         self.lastdiscard = self.linebuffer[nl:nl + nllen]
  544.         self.linebuffer = self.linebuffer[nl + nllen:]
  545.  
  546.         # line is always returned with \n as newline char (except possibly
  547.         # for a final incomplete line in the file, which is handled above).
  548.         return buf + "\n"
  549.  
  550.     def readlines(self, sizehint = -1):
  551.         """Return a list with all (following) lines. The sizehint parameter
  552.         is ignored in this implementation.
  553.         """
  554.         result = []
  555.         while True:
  556.             line = self.readline()
  557.             if not line: break
  558.             result.append(line)
  559.         return result
  560.  
  561.     def read(self, size = None):
  562.         # act like file() obj and return empty string if size is 0
  563.         if size == 0:
  564.             return ''
  565.  
  566.         # determine read size
  567.         bytesToRead = self.compress_size - self.bytes_read
  568.  
  569.         # adjust read size for encrypted files since the first 12 bytes
  570.         # are for the encryption/password information
  571.         if self.decrypter is not None:
  572.             bytesToRead -= 12
  573.  
  574.         if size is not None and size >= 0:
  575.             if self.compress_type == ZIP_STORED:
  576.                 lr = len(self.readbuffer)
  577.                 bytesToRead = min(bytesToRead, size - lr)
  578.             elif self.compress_type == ZIP_DEFLATED:
  579.                 if len(self.readbuffer) > size:
  580.                     # the user has requested fewer bytes than we've already
  581.                     # pulled through the decompressor; don't read any more
  582.                     bytesToRead = 0
  583.                 else:
  584.                     # user will use up the buffer, so read some more
  585.                     lr = len(self.rawbuffer)
  586.                     bytesToRead = min(bytesToRead, self.compreadsize - lr)
  587.  
  588.         # avoid reading past end of file contents
  589.         if bytesToRead + self.bytes_read > self.compress_size:
  590.             bytesToRead = self.compress_size - self.bytes_read
  591.  
  592.         # try to read from file (if necessary)
  593.         if bytesToRead > 0:
  594.             bytes = self.fileobj.read(bytesToRead)
  595.             self.bytes_read += len(bytes)
  596.             self.rawbuffer += bytes
  597.  
  598.             # handle contents of raw buffer
  599.             if self.rawbuffer:
  600.                 newdata = self.rawbuffer
  601.                 self.rawbuffer = ''
  602.  
  603.                 # decrypt new data if we were given an object to handle that
  604.                 if newdata and self.decrypter is not None:
  605.                     newdata = ''.join(map(self.decrypter, newdata))
  606.  
  607.                 # decompress newly read data if necessary
  608.                 if newdata and self.compress_type == ZIP_DEFLATED:
  609.                     newdata = self.dc.decompress(newdata)
  610.                     self.rawbuffer = self.dc.unconsumed_tail
  611.                     if self.eof and len(self.rawbuffer) == 0:
  612.                         # we're out of raw bytes (both from the file and
  613.                         # the local buffer); flush just to make sure the
  614.                         # decompressor is done
  615.                         newdata += self.dc.flush()
  616.                         # prevent decompressor from being used again
  617.                         self.dc = None
  618.  
  619.                 self.readbuffer += newdata
  620.  
  621.  
  622.         # return what the user asked for
  623.         if size is None or len(self.readbuffer) <= size:
  624.             bytes = self.readbuffer
  625.             self.readbuffer = ''
  626.         else:
  627.             bytes = self.readbuffer[:size]
  628.             self.readbuffer = self.readbuffer[size:]
  629.  
  630.         return bytes
  631.  
  632.  
  633. class ZipFile:
  634.     """ Class with methods to open, read, write, close, list zip files.
  635.  
  636.     z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=False)
  637.  
  638.     file: Either the path to the file, or a file-like object.
  639.           If it is a path, the file will be opened and closed by ZipFile.
  640.     mode: The mode can be either read "r", write "w" or append "a".
  641.     compression: ZIP_STORED (no compression) or ZIP_DEFLATED (requires zlib).
  642.     allowZip64: if True ZipFile will create files with ZIP64 extensions when
  643.                 needed, otherwise it will raise an exception when this would
  644.                 be necessary.
  645.  
  646.     """
  647.  
  648.     fp = None                   # Set here since __del__ checks it
  649.  
  650.     def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=False):
  651.         """Open the ZIP file with mode read "r", write "w" or append "a"."""
  652.         if mode not in ("r", "w", "a"):
  653.             raise RuntimeError('ZipFile() requires mode "r", "w", or "a"')
  654.  
  655.         if compression == ZIP_STORED:
  656.             pass
  657.         elif compression == ZIP_DEFLATED:
  658.             if not zlib:
  659.                 raise RuntimeError,\
  660.                       "Compression requires the (missing) zlib module"
  661.         else:
  662.             raise RuntimeError, "That compression method is not supported"
  663.  
  664.         self._allowZip64 = allowZip64
  665.         self._didModify = False
  666.         self.debug = 0  # Level of printing: 0 through 3
  667.         self.NameToInfo = {}    # Find file info given name
  668.         self.filelist = []      # List of ZipInfo instances for archive
  669.         self.compression = compression  # Method of compression
  670.         self.mode = key = mode.replace('b', '')[0]
  671.         self.pwd = None
  672.         self.comment = ''
  673.  
  674.         # Check if we were passed a file-like object
  675.         if isinstance(file, basestring):
  676.             self._filePassed = 0
  677.             self.filename = file
  678.             modeDict = {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'}
  679.             try:
  680.                 self.fp = open(file, modeDict[mode])
  681.             except IOError:
  682.                 if mode == 'a':
  683.                     mode = key = 'w'
  684.                     self.fp = open(file, modeDict[mode])
  685.                 else:
  686.                     raise
  687.         else:
  688.             self._filePassed = 1
  689.             self.fp = file
  690.             self.filename = getattr(file, 'name', None)
  691.  
  692.         if key == 'r':
  693.             self._GetContents()
  694.         elif key == 'w':
  695.             pass
  696.         elif key == 'a':
  697.             try:                        # See if file is a zip file
  698.                 self._RealGetContents()
  699.                 # seek to start of directory and overwrite
  700.                 self.fp.seek(self.start_dir, 0)
  701.             except BadZipfile:          # file is not a zip file, just append
  702.                 self.fp.seek(0, 2)
  703.         else:
  704.             if not self._filePassed:
  705.                 self.fp.close()
  706.                 self.fp = None
  707.             raise RuntimeError, 'Mode must be "r", "w" or "a"'
  708.  
  709.     def _GetContents(self):
  710.         """Read the directory, making sure we close the file if the format
  711.         is bad."""
  712.         try:
  713.             self._RealGetContents()
  714.         except BadZipfile:
  715.             if not self._filePassed:
  716.                 self.fp.close()
  717.                 self.fp = None
  718.             raise
  719.  
  720.     def _RealGetContents(self):
  721.         """Read in the table of contents for the ZIP file."""
  722.         fp = self.fp
  723.         endrec = _EndRecData(fp)
  724.         if not endrec:
  725.             raise BadZipfile, "File is not a zip file"
  726.         if self.debug > 1:
  727.             print endrec
  728.         size_cd = endrec[_ECD_SIZE]             # bytes in central directory
  729.         offset_cd = endrec[_ECD_OFFSET]         # offset of central directory
  730.         self.comment = endrec[_ECD_COMMENT]     # archive comment
  731.  
  732.         # "concat" is zero, unless zip was concatenated to another file
  733.         concat = endrec[_ECD_LOCATION] - size_cd - offset_cd
  734.         if endrec[_ECD_SIGNATURE] == stringEndArchive64:
  735.             # If Zip64 extension structures are present, account for them
  736.             concat -= (sizeEndCentDir64 + sizeEndCentDir64Locator)
  737.  
  738.         if self.debug > 2:
  739.             inferred = concat + offset_cd
  740.             print "given, inferred, offset", offset_cd, inferred, concat
  741.         # self.start_dir:  Position of start of central directory
  742.         self.start_dir = offset_cd + concat
  743.         fp.seek(self.start_dir, 0)
  744.         data = fp.read(size_cd)
  745.         fp = cStringIO.StringIO(data)
  746.         total = 0
  747.         while total < size_cd:
  748.             centdir = fp.read(sizeCentralDir)
  749.             if centdir[0:4] != stringCentralDir:
  750.                 raise BadZipfile, "Bad magic number for central directory"
  751.             centdir = struct.unpack(structCentralDir, centdir)
  752.             if self.debug > 2:
  753.                 print centdir
  754.             filename = fp.read(centdir[_CD_FILENAME_LENGTH])
  755.             # Create ZipInfo instance to store file information
  756.             x = ZipInfo(filename)
  757.             x.extra = fp.read(centdir[_CD_EXTRA_FIELD_LENGTH])
  758.             x.comment = fp.read(centdir[_CD_COMMENT_LENGTH])
  759.             x.header_offset = centdir[_CD_LOCAL_HEADER_OFFSET]
  760.             (x.create_version, x.create_system, x.extract_version, x.reserved,
  761.                 x.flag_bits, x.compress_type, t, d,
  762.                 x.CRC, x.compress_size, x.file_size) = centdir[1:12]
  763.             x.volume, x.internal_attr, x.external_attr = centdir[15:18]
  764.             # Convert date/time code to (year, month, day, hour, min, sec)
  765.             x._raw_time = t
  766.             x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F,
  767.                                      t>>11, (t>>5)&0x3F, (t&0x1F) * 2 )
  768.  
  769.             x._decodeExtra()
  770.             x.header_offset = x.header_offset + concat
  771.             x.filename = x._decodeFilename()
  772.             self.filelist.append(x)
  773.             self.NameToInfo[x.filename] = x
  774.  
  775.             # update total bytes read from central directory
  776.             total = (total + sizeCentralDir + centdir[_CD_FILENAME_LENGTH]
  777.                      + centdir[_CD_EXTRA_FIELD_LENGTH]
  778.                      + centdir[_CD_COMMENT_LENGTH])
  779.  
  780.             if self.debug > 2:
  781.                 print "total", total
  782.  
  783.  
  784.     def namelist(self):
  785.         """Return a list of file names in the archive."""
  786.         l = []
  787.         for data in self.filelist:
  788.             l.append(data.filename)
  789.         return l
  790.  
  791.     def infolist(self):
  792.         """Return a list of class ZipInfo instances for files in the
  793.         archive."""
  794.         return self.filelist
  795.  
  796.     def printdir(self):
  797.         """Print a table of contents for the zip file."""
  798.         print "%-46s %19s %12s" % ("File Name", "Modified    ", "Size")
  799.         for zinfo in self.filelist:
  800.             date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time[:6]
  801.             print "%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size)
  802.  
  803.     def testzip(self):
  804.         """Read all the files and check the CRC."""
  805.         chunk_size = 2 ** 20
  806.         for zinfo in self.filelist:
  807.             try:
  808.                 # Read by chunks, to avoid an OverflowError or a
  809.                 # MemoryError with very large embedded files.
  810.                 f = self.open(zinfo.filename, "r")
  811.                 while f.read(chunk_size):     # Check CRC-32
  812.                     pass
  813.             except BadZipfile:
  814.                 return zinfo.filename
  815.  
  816.     def getinfo(self, name):
  817.         """Return the instance of ZipInfo given 'name'."""
  818.         info = self.NameToInfo.get(name)
  819.         if info is None:
  820.             raise KeyError(
  821.                 'There is no item named %r in the archive' % name)
  822.  
  823.         return info
  824.  
  825.     def setpassword(self, pwd):
  826.         """Set default password for encrypted files."""
  827.         self.pwd = pwd
  828.  
  829.     def read(self, name, pwd=None):
  830.         """Return file bytes (as a string) for name."""
  831.         return self.open(name, "r", pwd).read()
  832.  
  833.     def open(self, name, mode="r", pwd=None):
  834.         """Return file-like object for 'name'."""
  835.         if mode not in ("r", "U", "rU"):
  836.             raise RuntimeError, 'open() requires mode "r", "U", or "rU"'
  837.         if not self.fp:
  838.             raise RuntimeError, \
  839.                   "Attempt to read ZIP archive that was already closed"
  840.  
  841.         # Only open a new file for instances where we were not
  842.         # given a file object in the constructor
  843.         if self._filePassed:
  844.             zef_file = self.fp
  845.         else:
  846.             zef_file = open(self.filename, 'rb')
  847.  
  848.         # Make sure we have an info object
  849.         if isinstance(name, ZipInfo):
  850.             # 'name' is already an info object
  851.             zinfo = name
  852.         else:
  853.             # Get info object for name
  854.             zinfo = self.getinfo(name)
  855.  
  856.         zef_file.seek(zinfo.header_offset, 0)
  857.  
  858.         # Skip the file header:
  859.         fheader = zef_file.read(sizeFileHeader)
  860.         if fheader[0:4] != stringFileHeader:
  861.             raise BadZipfile, "Bad magic number for file header"
  862.  
  863.         fheader = struct.unpack(structFileHeader, fheader)
  864.         fname = zef_file.read(fheader[_FH_FILENAME_LENGTH])
  865.         if fheader[_FH_EXTRA_FIELD_LENGTH]:
  866.             zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH])
  867.  
  868.         if fname != zinfo.orig_filename:
  869.             raise BadZipfile, \
  870.                       'File name in directory "%s" and header "%s" differ.' % (
  871.                           zinfo.orig_filename, fname)
  872.  
  873.         # check for encrypted flag & handle password
  874.         is_encrypted = zinfo.flag_bits & 0x1
  875.         zd = None
  876.         if is_encrypted:
  877.             if not pwd:
  878.                 pwd = self.pwd
  879.             if not pwd:
  880.                 raise RuntimeError, "File %s is encrypted, " \
  881.                       "password required for extraction" % name
  882.  
  883.             zd = _ZipDecrypter(pwd)
  884.             # The first 12 bytes in the cypher stream is an encryption header
  885.             #  used to strengthen the algorithm. The first 11 bytes are
  886.             #  completely random, while the 12th contains the MSB of the CRC,
  887.             #  or the MSB of the file time depending on the header type
  888.             #  and is used to check the correctness of the password.
  889.             bytes = zef_file.read(12)
  890.             h = map(zd, bytes[0:12])
  891.             if zinfo.flag_bits & 0x8:
  892.                 # compare against the file type from extended local headers
  893.                 check_byte = (zinfo._raw_time >> 8) & 0xff
  894.             else:
  895.                 # compare against the CRC otherwise
  896.                 check_byte = (zinfo.CRC >> 24) & 0xff
  897.             if ord(h[11]) != check_byte:
  898.                 raise RuntimeError("Bad password for file", name)
  899.  
  900.         # build and return a ZipExtFile
  901.         if zd is None:
  902.             zef = ZipExtFile(zef_file, zinfo)
  903.         else:
  904.             zef = ZipExtFile(zef_file, zinfo, zd)
  905.  
  906.         # set universal newlines on ZipExtFile if necessary
  907.         if "U" in mode:
  908.             zef.set_univ_newlines(True)
  909.         return zef
  910.  
  911.     def extract(self, member, path=None, pwd=None):
  912.         """Extract a member from the archive to the current working directory,
  913.            using its full name. Its file information is extracted as accurately
  914.            as possible. `member' may be a filename or a ZipInfo object. You can
  915.            specify a different directory using `path'.
  916.         """
  917.         if not isinstance(member, ZipInfo):
  918.             member = self.getinfo(member)
  919.  
  920.         if path is None:
  921.             path = os.getcwd()
  922.  
  923.         return self._extract_member(member, path, pwd)
  924.  
  925.     def extractall(self, path=None, members=None, pwd=None):
  926.         """Extract all members from the archive to the current working
  927.            directory. `path' specifies a different directory to extract to.
  928.            `members' is optional and must be a subset of the list returned
  929.            by namelist().
  930.         """
  931.         if members is None:
  932.             members = self.namelist()
  933.  
  934.         for zipinfo in members:
  935.             self.extract(zipinfo, path, pwd)
  936.  
  937.     def _extract_member(self, member, targetpath, pwd):
  938.         """Extract the ZipInfo object 'member' to a physical
  939.            file on the path targetpath.
  940.         """
  941.         # build the destination pathname, replacing
  942.         # forward slashes to platform specific separators.
  943.         # Strip trailing path separator, unless it represents the root.
  944.         if (targetpath[-1:] in (os.path.sep, os.path.altsep)
  945.             and len(os.path.splitdrive(targetpath)[1]) > 1):
  946.             targetpath = targetpath[:-1]
  947.  
  948.         # don't include leading "/" from file name if present
  949.         if member.filename[0] == '/':
  950.             targetpath = os.path.join(targetpath, member.filename[1:])
  951.         else:
  952.             targetpath = os.path.join(targetpath, member.filename)
  953.  
  954.         targetpath = os.path.normpath(targetpath)
  955.  
  956.         # Create all upper directories if necessary.
  957.         upperdirs = os.path.dirname(targetpath)
  958.         if upperdirs and not os.path.exists(upperdirs):
  959.             os.makedirs(upperdirs)
  960.  
  961.         if member.filename[-1] == '/':
  962.             if not os.path.isdir(targetpath):
  963.                 os.mkdir(targetpath)
  964.             return targetpath
  965.  
  966.         source = self.open(member, pwd=pwd)
  967.         target = file(targetpath, "wb")
  968.         shutil.copyfileobj(source, target)
  969.         source.close()
  970.         target.close()
  971.  
  972.         return targetpath
  973.  
  974.     def _writecheck(self, zinfo):
  975.         """Check for errors before writing a file to the archive."""
  976.         if zinfo.filename in self.NameToInfo:
  977.             if self.debug:      # Warning for duplicate names
  978.                 print "Duplicate name:", zinfo.filename
  979.         if self.mode not in ("w", "a"):
  980.             raise RuntimeError, 'write() requires mode "w" or "a"'
  981.         if not self.fp:
  982.             raise RuntimeError, \
  983.                   "Attempt to write ZIP archive that was already closed"
  984.         if zinfo.compress_type == ZIP_DEFLATED and not zlib:
  985.             raise RuntimeError, \
  986.                   "Compression requires the (missing) zlib module"
  987.         if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED):
  988.             raise RuntimeError, \
  989.                   "That compression method is not supported"
  990.         if zinfo.file_size > ZIP64_LIMIT:
  991.             if not self._allowZip64:
  992.                 raise LargeZipFile("Filesize would require ZIP64 extensions")
  993.         if zinfo.header_offset > ZIP64_LIMIT:
  994.             if not self._allowZip64:
  995.                 raise LargeZipFile("Zipfile size would require ZIP64 extensions")
  996.  
  997.     def write(self, filename, arcname=None, compress_type=None):
  998.         """Put the bytes from filename into the archive under the name
  999.         arcname."""
  1000.         if not self.fp:
  1001.             raise RuntimeError(
  1002.                   "Attempt to write to ZIP archive that was already closed")
  1003.  
  1004.         st = os.stat(filename)
  1005.         isdir = stat.S_ISDIR(st.st_mode)
  1006.         mtime = time.localtime(st.st_mtime)
  1007.         date_time = mtime[0:6]
  1008.         # Create ZipInfo instance to store file information
  1009.         if arcname is None:
  1010.             arcname = filename
  1011.         arcname = os.path.normpath(os.path.splitdrive(arcname)[1])
  1012.         while arcname[0] in (os.sep, os.altsep):
  1013.             arcname = arcname[1:]
  1014.         if isdir:
  1015.             arcname += '/'
  1016.         zinfo = ZipInfo(arcname, date_time)
  1017.         zinfo.external_attr = (st[0] & 0xFFFF) << 16L      # Unix attributes
  1018.         if compress_type is None:
  1019.             zinfo.compress_type = self.compression
  1020.         else:
  1021.             zinfo.compress_type = compress_type
  1022.  
  1023.         zinfo.file_size = st.st_size
  1024.         zinfo.flag_bits = 0x00
  1025.         zinfo.header_offset = self.fp.tell()    # Start of header bytes
  1026.  
  1027.         self._writecheck(zinfo)
  1028.         self._didModify = True
  1029.  
  1030.         if isdir:
  1031.             zinfo.file_size = 0
  1032.             zinfo.compress_size = 0
  1033.             zinfo.CRC = 0
  1034.             self.filelist.append(zinfo)
  1035.             self.NameToInfo[zinfo.filename] = zinfo
  1036.             self.fp.write(zinfo.FileHeader())
  1037.             return
  1038.  
  1039.         fp = open(filename, "rb")
  1040.         # Must overwrite CRC and sizes with correct data later
  1041.         zinfo.CRC = CRC = 0
  1042.         zinfo.compress_size = compress_size = 0
  1043.         zinfo.file_size = file_size = 0
  1044.         self.fp.write(zinfo.FileHeader())
  1045.         if zinfo.compress_type == ZIP_DEFLATED:
  1046.             cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
  1047.                  zlib.DEFLATED, -15)
  1048.         else:
  1049.             cmpr = None
  1050.         while 1:
  1051.             buf = fp.read(1024 * 8)
  1052.             if not buf:
  1053.                 break
  1054.             file_size = file_size + len(buf)
  1055.             CRC = crc32(buf, CRC) & 0xffffffff
  1056.             if cmpr:
  1057.                 buf = cmpr.compress(buf)
  1058.                 compress_size = compress_size + len(buf)
  1059.             self.fp.write(buf)
  1060.         fp.close()
  1061.         if cmpr:
  1062.             buf = cmpr.flush()
  1063.             compress_size = compress_size + len(buf)
  1064.             self.fp.write(buf)
  1065.             zinfo.compress_size = compress_size
  1066.         else:
  1067.             zinfo.compress_size = file_size
  1068.         zinfo.CRC = CRC
  1069.         zinfo.file_size = file_size
  1070.         # Seek backwards and write CRC and file sizes
  1071.         position = self.fp.tell()       # Preserve current position in file
  1072.         self.fp.seek(zinfo.header_offset + 14, 0)
  1073.         self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size,
  1074.               zinfo.file_size))
  1075.         self.fp.seek(position, 0)
  1076.         self.filelist.append(zinfo)
  1077.         self.NameToInfo[zinfo.filename] = zinfo
  1078.  
  1079.     def writestr(self, zinfo_or_arcname, bytes):
  1080.         """Write a file into the archive.  The contents is the string
  1081.         'bytes'.  'zinfo_or_arcname' is either a ZipInfo instance or
  1082.         the name of the file in the archive."""
  1083.         if not isinstance(zinfo_or_arcname, ZipInfo):
  1084.             zinfo = ZipInfo(filename=zinfo_or_arcname,
  1085.                             date_time=time.localtime(time.time())[:6])
  1086.             zinfo.compress_type = self.compression
  1087.             zinfo.external_attr = 0600 << 16
  1088.         else:
  1089.             zinfo = zinfo_or_arcname
  1090.  
  1091.         if not self.fp:
  1092.             raise RuntimeError(
  1093.                   "Attempt to write to ZIP archive that was already closed")
  1094.  
  1095.         zinfo.file_size = len(bytes)            # Uncompressed size
  1096.         zinfo.header_offset = self.fp.tell()    # Start of header bytes
  1097.         self._writecheck(zinfo)
  1098.         self._didModify = True
  1099.         zinfo.CRC = crc32(bytes) & 0xffffffff       # CRC-32 checksum
  1100.         if zinfo.compress_type == ZIP_DEFLATED:
  1101.             co = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
  1102.                  zlib.DEFLATED, -15)
  1103.             bytes = co.compress(bytes) + co.flush()
  1104.             zinfo.compress_size = len(bytes)    # Compressed size
  1105.         else:
  1106.             zinfo.compress_size = zinfo.file_size
  1107.         zinfo.header_offset = self.fp.tell()    # Start of header bytes
  1108.         self.fp.write(zinfo.FileHeader())
  1109.         self.fp.write(bytes)
  1110.         self.fp.flush()
  1111.         if zinfo.flag_bits & 0x08:
  1112.             # Write CRC and file sizes after the file data
  1113.             self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size,
  1114.                   zinfo.file_size))
  1115.         self.filelist.append(zinfo)
  1116.         self.NameToInfo[zinfo.filename] = zinfo
  1117.  
  1118.     def __del__(self):
  1119.         """Call the "close()" method in case the user forgot."""
  1120.         self.close()
  1121.  
  1122.     def close(self):
  1123.         """Close the file, and for mode "w" and "a" write the ending
  1124.         records."""
  1125.         if self.fp is None:
  1126.             return
  1127.  
  1128.         if self.mode in ("w", "a") and self._didModify: # write ending records
  1129.             count = 0
  1130.             pos1 = self.fp.tell()
  1131.             for zinfo in self.filelist:         # write central directory
  1132.                 count = count + 1
  1133.                 dt = zinfo.date_time
  1134.                 dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
  1135.                 dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
  1136.                 extra = []
  1137.                 if zinfo.file_size > ZIP64_LIMIT \
  1138.                         or zinfo.compress_size > ZIP64_LIMIT:
  1139.                     extra.append(zinfo.file_size)
  1140.                     extra.append(zinfo.compress_size)
  1141.                     file_size = 0xffffffff
  1142.                     compress_size = 0xffffffff
  1143.                 else:
  1144.                     file_size = zinfo.file_size
  1145.                     compress_size = zinfo.compress_size
  1146.  
  1147.                 if zinfo.header_offset > ZIP64_LIMIT:
  1148.                     extra.append(zinfo.header_offset)
  1149.                     header_offset = 0xffffffffL
  1150.                 else:
  1151.                     header_offset = zinfo.header_offset
  1152.  
  1153.                 extra_data = zinfo.extra
  1154.                 if extra:
  1155.                     # Append a ZIP64 field to the extra's
  1156.                     extra_data = struct.pack(
  1157.                             '<HH' + 'Q'*len(extra),
  1158.                             1, 8*len(extra), *extra) + extra_data
  1159.  
  1160.                     extract_version = max(45, zinfo.extract_version)
  1161.                     create_version = max(45, zinfo.create_version)
  1162.                 else:
  1163.                     extract_version = zinfo.extract_version
  1164.                     create_version = zinfo.create_version
  1165.  
  1166.                 try:
  1167.                     filename, flag_bits = zinfo._encodeFilenameFlags()
  1168.                     centdir = struct.pack(structCentralDir,
  1169.                      stringCentralDir, create_version,
  1170.                      zinfo.create_system, extract_version, zinfo.reserved,
  1171.                      flag_bits, zinfo.compress_type, dostime, dosdate,
  1172.                      zinfo.CRC, compress_size, file_size,
  1173.                      len(filename), len(extra_data), len(zinfo.comment),
  1174.                      0, zinfo.internal_attr, zinfo.external_attr,
  1175.                      header_offset)
  1176.                 except DeprecationWarning:
  1177.                     print >>sys.stderr, (structCentralDir,
  1178.                      stringCentralDir, create_version,
  1179.                      zinfo.create_system, extract_version, zinfo.reserved,
  1180.                      zinfo.flag_bits, zinfo.compress_type, dostime, dosdate,
  1181.                      zinfo.CRC, compress_size, file_size,
  1182.                      len(zinfo.filename), len(extra_data), len(zinfo.comment),
  1183.                      0, zinfo.internal_attr, zinfo.external_attr,
  1184.                      header_offset)
  1185.                     raise
  1186.                 self.fp.write(centdir)
  1187.                 self.fp.write(filename)
  1188.                 self.fp.write(extra_data)
  1189.                 self.fp.write(zinfo.comment)
  1190.  
  1191.             pos2 = self.fp.tell()
  1192.             # Write end-of-zip-archive record
  1193.             centDirCount = count
  1194.             centDirSize = pos2 - pos1
  1195.             centDirOffset = pos1
  1196.             if (centDirCount >= ZIP_FILECOUNT_LIMIT or
  1197.                 centDirOffset > ZIP64_LIMIT or
  1198.                 centDirSize > ZIP64_LIMIT):
  1199.                 # Need to write the ZIP64 end-of-archive records
  1200.                 zip64endrec = struct.pack(
  1201.                         structEndArchive64, stringEndArchive64,
  1202.                         44, 45, 45, 0, 0, centDirCount, centDirCount,
  1203.                         centDirSize, centDirOffset)
  1204.                 self.fp.write(zip64endrec)
  1205.  
  1206.                 zip64locrec = struct.pack(
  1207.                         structEndArchive64Locator,
  1208.                         stringEndArchive64Locator, 0, pos2, 1)
  1209.                 self.fp.write(zip64locrec)
  1210.                 centDirCount = min(centDirCount, 0xFFFF)
  1211.                 centDirSize = min(centDirSize, 0xFFFFFFFF)
  1212.                 centDirOffset = min(centDirOffset, 0xFFFFFFFF)
  1213.  
  1214.             # check for valid comment length
  1215.             if len(self.comment) >= ZIP_MAX_COMMENT:
  1216.                 if self.debug > 0:
  1217.                     msg = 'Archive comment is too long; truncating to %d bytes' \
  1218.                           % ZIP_MAX_COMMENT
  1219.                 self.comment = self.comment[:ZIP_MAX_COMMENT]
  1220.  
  1221.             endrec = struct.pack(structEndArchive, stringEndArchive,
  1222.                                  0, 0, centDirCount, centDirCount,
  1223.                                  centDirSize, centDirOffset, len(self.comment))
  1224.             self.fp.write(endrec)
  1225.             self.fp.write(self.comment)
  1226.             self.fp.flush()
  1227.  
  1228.         if not self._filePassed:
  1229.             self.fp.close()
  1230.         self.fp = None
  1231.  
  1232.  
  1233. class PyZipFile(ZipFile):
  1234.     """Class to create ZIP archives with Python library files and packages."""
  1235.  
  1236.     def writepy(self, pathname, basename = ""):
  1237.         """Add all files from "pathname" to the ZIP archive.
  1238.  
  1239.         If pathname is a package directory, search the directory and
  1240.         all package subdirectories recursively for all *.py and enter
  1241.         the modules into the archive.  If pathname is a plain
  1242.         directory, listdir *.py and enter all modules.  Else, pathname
  1243.         must be a Python *.py file and the module will be put into the
  1244.         archive.  Added modules are always module.pyo or module.pyc.
  1245.         This method will compile the module.py into module.pyc if
  1246.         necessary.
  1247.         """
  1248.         dir, name = os.path.split(pathname)
  1249.         if os.path.isdir(pathname):
  1250.             initname = os.path.join(pathname, "__init__.py")
  1251.             if os.path.isfile(initname):
  1252.                 # This is a package directory, add it
  1253.                 if basename:
  1254.                     basename = "%s/%s" % (basename, name)
  1255.                 else:
  1256.                     basename = name
  1257.                 if self.debug:
  1258.                     print "Adding package in", pathname, "as", basename
  1259.                 fname, arcname = self._get_codename(initname[0:-3], basename)
  1260.                 if self.debug:
  1261.                     print "Adding", arcname
  1262.                 self.write(fname, arcname)
  1263.                 dirlist = os.listdir(pathname)
  1264.                 dirlist.remove("__init__.py")
  1265.                 # Add all *.py files and package subdirectories
  1266.                 for filename in dirlist:
  1267.                     path = os.path.join(pathname, filename)
  1268.                     root, ext = os.path.splitext(filename)
  1269.                     if os.path.isdir(path):
  1270.                         if os.path.isfile(os.path.join(path, "__init__.py")):
  1271.                             # This is a package directory, add it
  1272.                             self.writepy(path, basename)  # Recursive call
  1273.                     elif ext == ".py":
  1274.                         fname, arcname = self._get_codename(path[0:-3],
  1275.                                          basename)
  1276.                         if self.debug:
  1277.                             print "Adding", arcname
  1278.                         self.write(fname, arcname)
  1279.             else:
  1280.                 # This is NOT a package directory, add its files at top level
  1281.                 if self.debug:
  1282.                     print "Adding files from directory", pathname
  1283.                 for filename in os.listdir(pathname):
  1284.                     path = os.path.join(pathname, filename)
  1285.                     root, ext = os.path.splitext(filename)
  1286.                     if ext == ".py":
  1287.                         fname, arcname = self._get_codename(path[0:-3],
  1288.                                          basename)
  1289.                         if self.debug:
  1290.                             print "Adding", arcname
  1291.                         self.write(fname, arcname)
  1292.         else:
  1293.             if pathname[-3:] != ".py":
  1294.                 raise RuntimeError, \
  1295.                       'Files added with writepy() must end with ".py"'
  1296.             fname, arcname = self._get_codename(pathname[0:-3], basename)
  1297.             if self.debug:
  1298.                 print "Adding file", arcname
  1299.             self.write(fname, arcname)
  1300.  
  1301.     def _get_codename(self, pathname, basename):
  1302.         """Return (filename, archivename) for the path.
  1303.  
  1304.         Given a module name path, return the correct file path and
  1305.         archive name, compiling if necessary.  For example, given
  1306.         /python/lib/string, return (/python/lib/string.pyc, string).
  1307.         """
  1308.         file_py  = pathname + ".py"
  1309.         file_pyc = pathname + ".pyc"
  1310.         file_pyo = pathname + ".pyo"
  1311.         if os.path.isfile(file_pyo) and \
  1312.                             os.stat(file_pyo).st_mtime >= os.stat(file_py).st_mtime:
  1313.             fname = file_pyo    # Use .pyo file
  1314.         elif not os.path.isfile(file_pyc) or \
  1315.              os.stat(file_pyc).st_mtime < os.stat(file_py).st_mtime:
  1316.             import py_compile
  1317.             if self.debug:
  1318.                 print "Compiling", file_py
  1319.             try:
  1320.                 py_compile.compile(file_py, file_pyc, None, True)
  1321.             except py_compile.PyCompileError,err:
  1322.                 print err.msg
  1323.             fname = file_pyc
  1324.         else:
  1325.             fname = file_pyc
  1326.         archivename = os.path.split(fname)[1]
  1327.         if basename:
  1328.             archivename = "%s/%s" % (basename, archivename)
  1329.         return (fname, archivename)
  1330.  
  1331.  
  1332. def main(args = None):
  1333.     import textwrap
  1334.     USAGE=textwrap.dedent("""\
  1335.         Usage:
  1336.             zipfile.py -l zipfile.zip        # Show listing of a zipfile
  1337.             zipfile.py -t zipfile.zip        # Test if a zipfile is valid
  1338.             zipfile.py -e zipfile.zip target # Extract zipfile into target dir
  1339.             zipfile.py -c zipfile.zip src ... # Create zipfile from sources
  1340.         """)
  1341.     if args is None:
  1342.         args = sys.argv[1:]
  1343.  
  1344.     if not args or args[0] not in ('-l', '-c', '-e', '-t'):
  1345.         print USAGE
  1346.         sys.exit(1)
  1347.  
  1348.     if args[0] == '-l':
  1349.         if len(args) != 2:
  1350.             print USAGE
  1351.             sys.exit(1)
  1352.         zf = ZipFile(args[1], 'r')
  1353.         zf.printdir()
  1354.         zf.close()
  1355.  
  1356.     elif args[0] == '-t':
  1357.         if len(args) != 2:
  1358.             print USAGE
  1359.             sys.exit(1)
  1360.         zf = ZipFile(args[1], 'r')
  1361.         zf.testzip()
  1362.         print "Done testing"
  1363.  
  1364.     elif args[0] == '-e':
  1365.         if len(args) != 3:
  1366.             print USAGE
  1367.             sys.exit(1)
  1368.  
  1369.         zf = ZipFile(args[1], 'r')
  1370.         out = args[2]
  1371.         for path in zf.namelist():
  1372.             if path.startswith('./'):
  1373.                 tgt = os.path.join(out, path[2:])
  1374.             else:
  1375.                 tgt = os.path.join(out, path)
  1376.  
  1377.             tgtdir = os.path.dirname(tgt)
  1378.             if not os.path.exists(tgtdir):
  1379.                 os.makedirs(tgtdir)
  1380.             fp = open(tgt, 'wb')
  1381.             fp.write(zf.read(path))
  1382.             fp.close()
  1383.         zf.close()
  1384.  
  1385.     elif args[0] == '-c':
  1386.         if len(args) < 3:
  1387.             print USAGE
  1388.             sys.exit(1)
  1389.  
  1390.         def addToZip(zf, path, zippath):
  1391.             if os.path.isfile(path):
  1392.                 zf.write(path, zippath, ZIP_DEFLATED)
  1393.             elif os.path.isdir(path):
  1394.                 for nm in os.listdir(path):
  1395.                     addToZip(zf,
  1396.                             os.path.join(path, nm), os.path.join(zippath, nm))
  1397.             # else: ignore
  1398.  
  1399.         zf = ZipFile(args[1], 'w', allowZip64=True)
  1400.         for src in args[2:]:
  1401.             addToZip(zf, src, os.path.basename(src))
  1402.  
  1403.         zf.close()
  1404.  
  1405. if __name__ == "__main__":
  1406.     main()
  1407.